home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / c / etc / RCS / fsDispatch.c,v < prev    next >
Text File  |  1990-03-29  |  28KB  |  1,121 lines

  1. head     1.7;
  2. branch   ;
  3. access   ;
  4. symbols  ;
  5. locks    ; strict;
  6. comment  @ * @;
  7.  
  8.  
  9. 1.7
  10. date     89.11.22.16.34.29;  author ouster;  state Exp;
  11. branches ;
  12. next     1.6;
  13.  
  14. 1.6
  15. date     89.05.19.14.15.20;  author ouster;  state Exp;
  16. branches ;
  17. next     1.5;
  18.  
  19. 1.5
  20. date     89.05.19.11.53.14;  author ouster;  state Exp;
  21. branches ;
  22. next     1.4;
  23.  
  24. 1.4
  25. date     89.05.19.11.21.19;  author jhh;  state Exp;
  26. branches ;
  27. next     1.3;
  28.  
  29. 1.3
  30. date     88.09.23.09.24.16;  author mendel;  state Exp;
  31. branches ;
  32. next     1.2;
  33.  
  34. 1.2
  35. date     88.07.25.10.42.35;  author ouster;  state Exp;
  36. branches ;
  37. next     1.1;
  38.  
  39. 1.1
  40. date     88.07.01.09.40.57;  author ouster;  state Exp;
  41. branches ;
  42. next     ;
  43.  
  44.  
  45. desc
  46. @@
  47.  
  48.  
  49. 1.7
  50. log
  51. @Don't panic in Fs_Dispatch if stream no longer in use:  handler
  52. could have been deleted by a previous event from same select.
  53. @
  54. text
  55. @/* 
  56.  * fsDispatch.c --
  57.  *
  58.  *    This file contains routines that implement a dispatcher for events
  59.  *    on streams and timeouts. The dispatcher handles the details of
  60.  *    waiting for events to occur on streams. When an event occurs, the
  61.  *    dispatcher calls a routine supplied by the clients to handle the
  62.  *    event. Also, timeout handlers can be created so that a client-supplied
  63.  *    routine can be called at a specific time or at regular intervals.
  64.  *
  65.  * Copyright 1987 Regents of the University of California
  66.  * Permission to use, copy, modify, and distribute this
  67.  * software and its documentation for any purpose and without
  68.  * fee is hereby granted, provided that the above copyright
  69.  * notice appear in all copies.  The University of California
  70.  * makes no representations about the suitability of this
  71.  * software for any purpose.  It is provided "as is" without
  72.  * express or implied warranty.
  73.  */
  74.  
  75. #ifndef lint
  76. static char rcsid[] = "$Header: /sprite/src/lib/c/etc/RCS/fsDispatch.c,v 1.6 89/05/19 14:15:20 ouster Exp $ SPRITE (Berkeley)";
  77. #endif not lint
  78.  
  79. #include <sprite.h>
  80. #include <errno.h>
  81. #include <fs.h>
  82. #include <list.h>
  83. #include <bit.h>
  84. #include <spriteTime.h>
  85. #include <sys/time.h>
  86. #include <stdio.h>
  87. #include <stdlib.h>
  88. #include <string.h>
  89.  
  90.  
  91. /*
  92.  * The data structure used by Fs_EventHandler{Create,Destroy} and Fs_Dispatch 
  93.  * to manage event handlers for streams. The information is kept in an
  94.  * array that is indexed by the stream ID.
  95.  *
  96.  */
  97.  
  98. typedef struct {
  99.     void    (*proc)();    /* Routine to be called. */
  100.     ClientData    data;        /* Data passed to proc. */
  101.     int        eventMask;    /* Mask of events cause proc to be called. */
  102.     Boolean    inUse;        /* Consistency check. */
  103. } StreamInfo;
  104.  
  105. #define MAX_NUM_STREAMS        256
  106. static StreamInfo infoArray[MAX_NUM_STREAMS];
  107.  
  108.  
  109. /*
  110.  * Bit arrays for use with select. ReadMask, writeMask and exceptMask
  111.  * are the master copies of the masks -- they are updated by 
  112.  * Fs_EventHandlerCreate and Fs_EventHandlerDestroy.
  113.  */
  114.  
  115. #define MASK_SIZE    (Bit_NumInts(MAX_NUM_STREAMS))
  116. static int    readMask[MASK_SIZE];
  117. static int    writeMask[MASK_SIZE];
  118. static int    exceptMask[MASK_SIZE];
  119.  
  120. /*
  121.  * MaxPossNumStreams is the maximum value for the number of active streams. 
  122.  * For example if streams 0, 1, and 5 are the only active streams, 
  123.  * maxPossNumStreams should equal to 6 because streams 0-5 could active.
  124.  */
  125. static int    maxPossNumStreams = -1;
  126.  
  127.  
  128. /*
  129.  * The data structure used by Fs_TimeoutHandler{Create,Destroy},
  130.  * CallTimeoutHandler and ComputeTimeoutValue to manage timeout handlers.
  131.  * The information is kept in a doubly-linked list that is sorted on 
  132.  * the time field.
  133.  */
  134.  
  135. typedef struct {
  136.     List_Links    links;
  137.     void    (*proc)();    /* Routine to be called. */
  138.     ClientData    data;        /* Data passed to proc. */
  139.     Time    time;        /* Absolute time when proc should be called. */
  140.     Time    interval;    /* Relative time when proc should be called. */
  141.     Boolean    resched;    /* If TRUE, reschedule the proc to be called
  142.                  * again at the next interval. */
  143. } TimeoutInfo;
  144. static List_Links    timeoutList;
  145.  
  146. /*
  147.  * Forever is the amount of time to wait until the next timeout
  148.  * (a very, very long time).
  149.  */
  150. static Time forever = { 0x7fffffff, 0 };
  151.  
  152. /*
  153.  * MIN_TIMEOUT is the # of microseconds that must past before the next
  154.  * call to a timeout handler, i.e. the minimum interval between calls to
  155.  * timeout handler. This is used to make sure the dispatcher doesn't
  156.  * spend all its time handling timeouts.
  157.  *
  158.  * 40000 microseconds = 40 milliseconds.
  159.  */
  160.  
  161. #define MIN_TIMEOUT  40000
  162.  
  163. /*
  164.  * A flag to indicate if DispatcherInit() has been called or not.
  165.  */
  166. static Boolean initialized = FALSE;
  167.  
  168. /*
  169.  * Statistics about what type of event (stream or timeout) occurs
  170.  * during each call to Fs_Dispatch().
  171.  */
  172. unsigned int fsNumTimeoutEvents = 0;
  173. unsigned int fsNumStreamEvents = 0;
  174.  
  175. /*
  176.  * Forward references.
  177.  */
  178.  
  179. static void    SearchMask();
  180. static void    CallTimeoutHandler();
  181. static Boolean    ComputeTimeoutValue();
  182. static void    DispatcherInit();
  183.  
  184.  
  185. /*
  186.  *----------------------------------------------------------------------
  187.  *
  188.  * DispatcherInit --
  189.  *
  190.  *    Initializes the data structures necessary to manage the event handlers,
  191.  *    the timer queue of procedures and the select bitmasks. 
  192.  *
  193.  * Results:
  194.  *    None.
  195.  *
  196.  * Side effects:
  197.  *    The infoArray and timer queue list are initialized.
  198.  *    The select bitmasks are set to 0.
  199.  *
  200.  *----------------------------------------------------------------------
  201.  */
  202.  
  203. static void
  204. DispatcherInit()
  205. {
  206.     initialized = TRUE;
  207.  
  208.     bzero((char *) infoArray, sizeof(StreamInfo) * MAX_NUM_STREAMS);
  209.     List_Init(&timeoutList);
  210.  
  211.     Bit_Zero(MAX_NUM_STREAMS, readMask);
  212.     Bit_Zero(MAX_NUM_STREAMS, writeMask);
  213.     Bit_Zero(MAX_NUM_STREAMS, exceptMask);
  214. }
  215.  
  216.  
  217. /*
  218.  *----------------------------------------------------------------------
  219.  *
  220.  * Fs_Dispatch --
  221.  *
  222.  *      This routine calls select to wait for a timeout or for a stream
  223.  *      to become readable, writable and/or has an exception condition
  224.  *      pending.  It looks at the results returned by select, and calls
  225.  *      client routines to handle the events.  Client routines must have
  226.  *      been pre-registered by calling Fs_EventHandlerCreate or
  227.  *      Fs_TimeoutHandlerCreate.
  228.  *
  229.  *    The readiness event handlers are called in ascending order 
  230.  *    based on the stream ID, regardless of the order of calls to 
  231.  *    Fs_EventHandlerCreate. However, the timeout handlers are
  232.  *      called in the order determined by the ordering of calls
  233.  *      to Fs_TimeoutHandlerCreate.
  234.  *
  235.  *    The routine will return after handling the events from a
  236.  *    single select call.
  237.  *
  238.  * Results:
  239.  *    None.
  240.  *
  241.  * Side effects:
  242.  *    The called routines may cause side-effects.
  243.  *
  244.  *----------------------------------------------------------------------
  245.  */
  246.  
  247. void
  248. Fs_Dispatch()
  249. {
  250.     register int    streamID;
  251.     Time        timeout;
  252.     Time        *timeoutPtr;
  253.     int            numReady;
  254.     int            tempReadMask[MASK_SIZE];
  255.     int            tempWriteMask[MASK_SIZE];
  256.     int            tempExceptMask[MASK_SIZE];
  257.  
  258.     if (!initialized) {
  259.     panic("Fs_Dispatch: not initialized yet.\n");
  260.     }
  261.  
  262.     /*
  263.      * First compute how time must elapse before the next routine
  264.      * on the timeout queue should be called. If ComputeTimeoutValue()
  265.      * determines that a routine should be called immediately, it
  266.      * returns TRUE and we go to CallTimeoutHandler() to call the routine.
  267.      */
  268.     
  269.     if (ComputeTimeoutValue(&timeout)) {
  270.     CallTimeoutHandler();
  271.     fsNumTimeoutEvents++;
  272.     return;
  273.     }
  274.  
  275.     if (Time_EQ(timeout, forever)) {
  276.     /*
  277.      * Nothing on the timeout queue.
  278.      */
  279.  
  280.     if (maxPossNumStreams == 0) {
  281.         panic("Fs_Dispatch: nothing to do.\n");
  282.         return;
  283.     } else {
  284.         timeoutPtr = (Time *) NULL;
  285.     }
  286.     } else {
  287.     if (maxPossNumStreams == 0) {
  288.         /*
  289.          * No streams to select so just wait until the next routine
  290.          * on the timeout queue needs to be called.
  291.          */
  292.  
  293.         select(0, (int *) NULL, (int *) NULL, (int *) NULL,
  294.             (struct timeval *) &timeout);
  295.         CallTimeoutHandler();
  296.         fsNumTimeoutEvents++;
  297.         return;
  298.     } else {
  299.         timeoutPtr = &timeout;
  300.     }
  301.     }
  302.  
  303.  
  304.     /*
  305.      * Wait for an event on 1 or more streams or until a timeout
  306.      * period has expired.
  307.      */
  308.  
  309.     Bit_Zero(MAX_NUM_STREAMS, tempReadMask);
  310.     Bit_Zero(MAX_NUM_STREAMS, tempWriteMask);
  311.     Bit_Zero(MAX_NUM_STREAMS, tempExceptMask);
  312.  
  313.     Bit_Copy(maxPossNumStreams, readMask, tempReadMask);
  314.     Bit_Copy(maxPossNumStreams, writeMask, tempWriteMask);
  315.     Bit_Copy(maxPossNumStreams, exceptMask, tempExceptMask);
  316.  
  317.  
  318.     numReady = select(maxPossNumStreams, tempReadMask, tempWriteMask,
  319.         tempExceptMask, (struct timeval *) timeoutPtr);
  320.  
  321.     if (numReady == 0) {
  322.     /*
  323.      * Nothing happened on the streams but a routine in the timeout
  324.      * queue needs to be called now.
  325.      */
  326.     CallTimeoutHandler();
  327.     fsNumTimeoutEvents++;
  328.  
  329.     } else if (numReady < 0) {
  330.     if (errno != EINTR) {
  331.         fprintf(stderr, "Fs_Dispatch select error: %s\n", strerror(errno));
  332.         exit(1);
  333.     }
  334.  
  335.     } else {
  336.     register int    event;
  337.     register StreamInfo    *infoPtr;
  338.  
  339.     fsNumStreamEvents++;
  340.  
  341.     /*
  342.      * Something happened on a stream (or streams). Go through
  343.      * the masks to find out which streams need attention.
  344.      * Call the routine to handle the event on the stream.
  345.      *
  346.      * We want to call the handler just once so when searching through
  347.      * the read mask, see if the stream is also writable and has an
  348.      * exception condition pending, and likewise, in the writable mask
  349.      * search see if the stream also has an exception condition pending.
  350.      * This ensures that a handler is called only once, with the
  351.      * eventMask containing 1 to 3 events. A simpler search technique
  352.      * is to search the 3 masks independently but then a handler could
  353.      * potentially be called up to 3 times. The first technique is
  354.      * better because the handler can determine the order of how it
  355.      * handles the events.
  356.      */
  357.  
  358.     streamID = Bit_FindFirstSet(maxPossNumStreams, tempReadMask);
  359.     while (streamID != -1) {
  360.         if (streamID < 0 || streamID > MAX_NUM_STREAMS) {
  361.         panic("Fs_Dispatch: stream ID %d out of range\n", streamID);
  362.         }
  363.  
  364.         infoPtr = &(infoArray[streamID]);
  365.  
  366.         /*
  367.          * There used to be code here to check inUse and panic
  368.          * if not set.  However, it's possible that one event
  369.          * handler could delete another event handler, causing
  370.          * inUse to be turned off in the second one before we
  371.          * get to this point.  Thus, don't check inUse here.
  372.          */
  373.  
  374.         event = FS_READABLE;
  375.         Bit_Clear(streamID, tempReadMask);
  376.  
  377.         if (Bit_IsSet(streamID, tempWriteMask)) {
  378.         event |= FS_WRITABLE;
  379.         Bit_Clear(streamID, tempWriteMask);
  380.         }
  381.         if (Bit_IsSet(streamID, tempExceptMask)) {
  382.         event |= FS_EXCEPTION;
  383.         Bit_Clear(streamID, tempExceptMask);
  384.         }
  385.  
  386.         if (infoPtr->eventMask & event) {
  387.         (*infoPtr->proc) (infoPtr->data, streamID, event);
  388.         }
  389.  
  390.         streamID = Bit_FindFirstSet(maxPossNumStreams, tempReadMask);
  391.     }
  392.  
  393.     streamID = Bit_FindFirstSet(maxPossNumStreams, tempWriteMask);
  394.     while (streamID != -1) {
  395.         if (streamID < 0 || streamID > MAX_NUM_STREAMS) {
  396.         panic("Fs_Dispatch: stream ID %d out of range\n", streamID);
  397.         }
  398.  
  399.         infoPtr = &(infoArray[streamID]);
  400.         event = FS_WRITABLE;
  401.         Bit_Clear(streamID, tempWriteMask);
  402.  
  403.         if (Bit_IsSet(streamID, tempExceptMask)) {
  404.         event |= FS_EXCEPTION;
  405.         Bit_Clear(streamID, tempExceptMask);
  406.         }
  407.  
  408.         if (infoPtr->eventMask & event) {
  409.         (*infoPtr->proc) (infoPtr->data, streamID, event);
  410.         }
  411.  
  412.         streamID = Bit_FindFirstSet(maxPossNumStreams, tempWriteMask);
  413.     }
  414.  
  415.     streamID = Bit_FindFirstSet(maxPossNumStreams, tempExceptMask);
  416.     while (streamID != -1) {
  417.         if (streamID < 0 || streamID > MAX_NUM_STREAMS) {
  418.         panic("Fs_Dispatch: stream ID %d out of range\n", streamID);
  419.         }
  420.  
  421.         infoPtr = &(infoArray[streamID]);
  422.         Bit_Clear(streamID, tempExceptMask);
  423.  
  424.         if (infoPtr->eventMask & FS_EXCEPTION) {
  425.         (*infoPtr->proc) (infoPtr->data, streamID, FS_EXCEPTION);
  426.         }
  427.  
  428.         streamID = Bit_FindFirstSet(maxPossNumStreams, tempExceptMask);
  429.     }
  430.     }
  431. }
  432.  
  433.  
  434. /*
  435.  *----------------------------------------------------------------------
  436.  *
  437.  * Fs_EventHandlerCreate --
  438.  *
  439.  *    Save the handler routine and data for a stream so it can be
  440.  *    called when the stream becomes ready in the main dispatch loop.
  441.  *    The handler "proc" should be declared as follows:
  442.  *
  443.  *    void
  444.  *    proc(clientData, streamID, eventMask)
  445.  *        ClientData    clientData;
  446.  *        int        streamID;
  447.  *        int        eventMask;
  448.  *    {
  449.  *    }
  450.  *
  451.  *    The association between a handler and a stream can be destroyed 
  452.  *    by calling Fs_EventHandlerDestroy.
  453.  *
  454.  * Results:
  455.  *    None.
  456.  *
  457.  * Side effects:
  458.  *    The infoArray and select masks are updated so events on the
  459.  *    stream will cause the proc routine to be called.
  460.  *
  461.  *----------------------------------------------------------------------
  462.  */
  463.  
  464. void
  465. Fs_EventHandlerCreate(streamID, eventMask, proc, clientData)
  466.     register int streamID;        /* Stream to watch. */
  467.     register int eventMask;        /* Events to watch for:  must be an
  468.                      * OR'ed combination of FS_READABLE,
  469.                      * FS_WRITABLE, or FS_EXCEPTION. */
  470.     void    (*proc)();        /* Procedure to call when an event in
  471.                      * eventMask occurs for streamID. */
  472.     ClientData    clientData;        /* Value to pass to proc. */
  473. {
  474.     if (!initialized) {
  475.     DispatcherInit();
  476.     }
  477.  
  478.     if (streamID < 0 || streamID > MAX_NUM_STREAMS) {
  479.     panic("Fs_EventHandlerCreate: stream ID %d out of range\n", streamID);
  480.     } 
  481.  
  482.     if ((eventMask & (FS_READABLE|FS_WRITABLE|FS_EXCEPTION)) == 0) {
  483.     panic("Fs_EventHandlerCreate: bad eventMask %x for stream %d\n", 
  484.         eventMask, streamID);
  485.     }
  486.  
  487.     if (infoArray[streamID].inUse) {
  488.     panic("Fs_EventHandlerCreate: stream ID %d already has a handler (0x%x)\n", 
  489.         streamID, infoArray[streamID].proc);
  490.     } 
  491.     infoArray[streamID].inUse = TRUE;
  492.     infoArray[streamID].proc = proc;
  493.     infoArray[streamID].data = clientData;
  494.     infoArray[streamID].eventMask = eventMask;
  495.  
  496.     if (eventMask & FS_READABLE) {
  497.     Bit_Set(streamID, readMask);
  498.     }
  499.     if (eventMask & FS_WRITABLE) {
  500.     Bit_Set(streamID, writeMask);
  501.     }
  502.     if (eventMask & FS_EXCEPTION) {
  503.     Bit_Set(streamID, exceptMask);
  504.     }
  505.  
  506.     /*
  507.      * Calculate the highest stream ID that's in use and add 1 to convert
  508.      * it into the # of bits in active use in the select masks.
  509.      */
  510.     if (streamID >= maxPossNumStreams) {
  511.     maxPossNumStreams = streamID + 1;
  512.     }
  513. }
  514.  
  515.  
  516. /*
  517.  *----------------------------------------------------------------------
  518.  *
  519.  * Fs_EventHandlerData --
  520.  *
  521.  *    Given an event ID for a stream, return the client data associated
  522.  *    with the event.
  523.  *
  524.  * Results:
  525.  *    The client data associated with the event.
  526.  *
  527.  * Side effects:
  528.  *    None.
  529.  *
  530.  *----------------------------------------------------------------------
  531.  */
  532.  
  533. ClientData
  534. Fs_EventHandlerData(streamID)
  535.     register int streamID;
  536. {
  537.     if (!initialized) {
  538.     panic("Fs_EventHandlerData: not initialized yet.\n");
  539.     }
  540.  
  541.     if (streamID < 0 || streamID > MAX_NUM_STREAMS) {
  542.     panic("Fs_EventHandlerData: stream ID %d out of range\n", streamID);
  543.     } 
  544.  
  545.     if (!infoArray[streamID].inUse) {
  546.     panic("Fs_EventHandlerData: stream ID %d not in use\n", streamID);
  547.     }
  548.  
  549.     return(infoArray[streamID].data);
  550. }
  551.  
  552.  
  553. /*
  554.  *----------------------------------------------------------------------
  555.  *
  556.  * Fs_EventHandlerChangeData --
  557.  *
  558.  *    Given an event ID for a stream, return the client data associated
  559.  *    with the event.
  560.  *
  561.  * Results:
  562.  *    The previous value of the client data associated with the event.
  563.  *
  564.  * Side effects:
  565.  *    None.
  566.  *
  567.  *----------------------------------------------------------------------
  568.  */
  569.  
  570. ClientData
  571. Fs_EventHandlerChangeData(streamID, newData)
  572.     register int streamID;
  573.     ClientData     newData;
  574. {
  575.     ClientData     oldData;
  576.  
  577.     if (!initialized) {
  578.     panic("Fs_EventHandlerChangeData: not initialized yet.\n");
  579.     }
  580.  
  581.     if (streamID < 0 || streamID > MAX_NUM_STREAMS) {
  582.     panic("Fs_EventHandlerChangeData: stream ID %d out of range\n", streamID);
  583.     } 
  584.  
  585.     if (!infoArray[streamID].inUse) {
  586.     panic("Fs_EventHandlerChangeData: stream ID %d not in use\n", streamID);
  587.     }
  588.  
  589.     oldData = infoArray[streamID].data;
  590.     infoArray[streamID].data = newData;
  591.     return(oldData);
  592. }
  593.  
  594.  
  595. /*
  596.  *----------------------------------------------------------------------
  597.  *
  598.  * Fs_EventHandlerDestroy --
  599.  *
  600.  *    Destroy the event handler routine for a stream.
  601.  *
  602.  * Results:
  603.  *    None.
  604.  *
  605.  * Side effects:
  606.  *    The infoArray and select masks are updated so events on
  607.  *    streamID will be ignored.
  608.  *
  609.  *----------------------------------------------------------------------
  610.  */
  611.  
  612. void
  613. Fs_EventHandlerDestroy(streamID)
  614.     register int streamID;
  615. {
  616.     register int eventMask;
  617.  
  618.     if (!initialized) {
  619.     panic("Fs_EventHandlerDestroy: not initialized yet.\n");
  620.     }
  621.  
  622.     if (streamID < 0 || streamID > MAX_NUM_STREAMS) {
  623.     panic("Fs_EventHandlerDestroy: stream ID %d out of range\n", streamID);
  624.     } 
  625.  
  626.     infoArray[streamID].inUse = FALSE;
  627.     eventMask = infoArray[streamID].eventMask;
  628.     infoArray[streamID].eventMask = 0;
  629.  
  630.     if (eventMask & FS_READABLE) {
  631.     Bit_Clear(streamID, readMask);
  632.     }
  633.     if (eventMask & FS_WRITABLE) {
  634.     Bit_Clear(streamID, writeMask);
  635.     }
  636.     if (eventMask & FS_EXCEPTION) {
  637.     Bit_Clear(streamID, exceptMask);
  638.     }
  639.  
  640.     /*
  641.      * Adjust maxPossNumStreams if this stream was the highest one in use.
  642.      */
  643.     if (streamID == (maxPossNumStreams - 1)) {
  644.     do {
  645.         maxPossNumStreams--;
  646.     } while ((maxPossNumStreams >= 1) && 
  647.             (!infoArray[maxPossNumStreams - 1].inUse));
  648.     }
  649. }
  650.  
  651.  
  652. /*
  653.  *----------------------------------------------------------------------
  654.  *
  655.  * Fs_TimeoutHandlerCreate --
  656.  *
  657.  *    Schedules a routine to be called at a certain time by adding 
  658.  *    it to the timer queue. 
  659.  *
  660.  *    When the client routine is called at its scheduled time, it is 
  661.  *    passed two parameters:
  662.  *     a) the clientData argument passed into this routine, and
  663.  *     b) the time it is scheduled to be called at.
  664.  *    Hence the routine should be declared as:
  665.  *
  666.  *        void
  667.  *        proc(clientData, time)
  668.  *        ClientData clientData;
  669.  *        Time time;
  670.  *        {}
  671.  *
  672.  *    The time a routine should be called at can be specified in two
  673.  *    ways: an absolute time (e.g. 12:00 4 July 1776) or an interval.
  674.  *    For example, to have proc called in 1 hour from now and every hour 
  675.  *    after that, the call to Fs_TimeoutHandlerCreate is:
  676.  *
  677.  *        Fs_TimeoutHandlerCreate(time_OneHour, TRUE, proc, clientData);
  678.  *
  679.  *      The 2nd argument (TRUE) to Fs_TimeoutHandlerCreate means the
  680.  *      routine will be called at the interval + the current time.
  681.  *      Routines scheduled with an interval will automatically be
  682.  *      rescheduled after they are called. They should deschedule themselves 
  683.  *      if they are not to be called more than once. Routines scheduled to
  684.  *      run at an absolute time will be called just once and must
  685.  *      reschedule themselves explicitly.
  686.  *
  687.  *    No error checking is done to make sure that a (proc, clientData) pair
  688.  *    is not put on the list more than once.
  689.  *
  690.  *
  691.  * Results:
  692.  *    A token that can be used to destroy the handler with 
  693.  *    Fs_TimeoutHandlerDestroy.
  694.  *
  695.  * Side effects:
  696.  *    The timeout queue is extended.
  697.  *
  698.  *----------------------------------------------------------------------
  699.  */
  700.  
  701. Fs_TimeoutHandler
  702. Fs_TimeoutHandlerCreate(time, relativeTime, proc, clientData)
  703.     Time     time;            /* See comments above. */
  704.     Boolean    relativeTime;
  705.     void    (*proc)();        /* Procedure to call. */
  706.     ClientData    clientData;        /* Value to pass to proc. */
  707. {
  708.     register TimeoutInfo *newPtr;
  709.     TimeoutInfo *itemPtr;
  710.     Boolean inserted;        /* TRUE if added to queue in FORALL loop. */
  711.  
  712.     if (!initialized) {
  713.     DispatcherInit();
  714.     }
  715.  
  716.     if (time.seconds == 0) {
  717.     /*
  718.      * Make sure the timeout value isn't too small so that we don't
  719.      * spend all our time calling timeout handlers.
  720.      */
  721.     if (time.microseconds < MIN_TIMEOUT) {
  722.         time.microseconds = MIN_TIMEOUT;
  723.     }
  724.     }
  725.  
  726.     newPtr = (TimeoutInfo *) malloc(sizeof(TimeoutInfo));
  727.  
  728.     if (relativeTime) {
  729.     /*
  730.      * Convert the interval into an absolute time by adding the 
  731.      * interval to the current time.
  732.      */
  733.     Time    curTime;
  734.  
  735.     (void) gettimeofday((struct timeval *) &curTime,
  736.         (struct timezone *) NULL);
  737.     Time_Add(curTime, time, &(newPtr->time));
  738.  
  739.     newPtr->interval = time;
  740.     newPtr->resched = TRUE;
  741.     } else {
  742.     newPtr->time = time;
  743.     newPtr->resched = FALSE;
  744.     }
  745.     newPtr->proc = proc;
  746.     newPtr->data = clientData;
  747.  
  748.     /*
  749.      *  Go through the timer queue and insert the new routine.  The queue
  750.      *  is ordered by the time field in the element.  The sooner the
  751.      *  routine needs to be called, the closer it is to the front of the
  752.      *  queue.  The new routine will not be added to the queue inside the
  753.      *  FOR loop if its scheduled time is after all elements in the queue
  754.      *  or the queue is empty.  It will be added after the last element in
  755.      *  the queue.
  756.      */
  757.  
  758.     inserted = FALSE;  /* assume new element not inserted inside FOR loop.*/
  759.     List_InitElement((List_Links *) newPtr);
  760.     LIST_FORALL(&timeoutList, (List_Links *) itemPtr) {
  761.  
  762.        if (Time_LT(newPtr->time, itemPtr->time)) {
  763.         List_Insert((List_Links *) newPtr, LIST_BEFORE(itemPtr));
  764.         inserted = TRUE;
  765.         break;
  766.     }
  767.     }
  768.     if (!inserted) {
  769.     List_Insert((List_Links *) newPtr, LIST_ATREAR(&timeoutList));
  770.     }
  771.  
  772.     return((Fs_TimeoutHandler) newPtr);
  773. }
  774.  
  775.  
  776. /*
  777.  *----------------------------------------------------------------------
  778.  *
  779.  * Fs_TimeoutHandlerDestroy --
  780.  *
  781.  *    Deschedules a routine that was to be called at a certain time 
  782.  *    by removing it from the timer queue.
  783.  *
  784.  *    It is not an error if the handler is not on the queue.
  785.  *
  786.  * Results:
  787.  *    None.
  788.  *
  789.  * Side effects:
  790.  *    The timer queue structure is updated. 
  791.  *
  792.  *----------------------------------------------------------------------
  793.  */
  794.  
  795. void
  796. Fs_TimeoutHandlerDestroy(token)
  797.     Fs_TimeoutHandler token;
  798. {
  799.     register List_Links *itemPtr;
  800.     TimeoutInfo *infoPtr = (TimeoutInfo *) token;
  801.  
  802.     if (!initialized) {
  803.     panic("Fs_TimeoutHandlerDestroy: not initialized yet.\n");
  804.     }
  805.  
  806.     if (infoPtr == (TimeoutInfo *) NULL) {
  807.     return;
  808.     }
  809.  
  810.     LIST_FORALL(&timeoutList, itemPtr) {
  811.  
  812.     if ((List_Links *) infoPtr == itemPtr) {
  813.         List_Remove(itemPtr);
  814.         break;
  815.     }
  816.     }
  817. }
  818.  
  819. /*
  820.  *----------------------------------------------------------------------
  821.  *
  822.  * CallTimeoutHandler --
  823.  *
  824.  *    Go through the timer queue and call the routines that are
  825.  *    scheduled to be called now.
  826.  *
  827.  * Results:
  828.  *    None.
  829.  *
  830.  * Side effects:
  831.  *    The called routine may cause side effects.
  832.  *
  833.  *----------------------------------------------------------------------
  834.  */
  835.  
  836. static void
  837. CallTimeoutHandler()
  838. {
  839.     register TimeoutInfo *readyPtr;    /* Ptr to handler that's ready to 
  840.                      * be called. */
  841.  
  842.     register TimeoutInfo *itemPtr;    /* Used to examine timeout queue. */
  843. #define itemListPtr    ((List_Links *) itemPtr)
  844.  
  845.     Time curTime;
  846.  
  847.     /*
  848.      *  The callback timer has expired. This means at least the first
  849.      *  routine on the timer queue is ready to be called.  Go through
  850.      *  the queue and call all routines that are scheduled to be
  851.      *  called. Since the queue is ordered by time, we can quit looking 
  852.      *  when we find the first routine that does not need to be called.
  853.      */
  854.  
  855.     if (!List_IsEmpty(&timeoutList)) {
  856.  
  857.     (void) gettimeofday((struct timeval *) &curTime,
  858.         (struct timezone *) NULL);
  859.  
  860.     itemListPtr = List_First(&timeoutList); 
  861.     while (!List_IsAtEnd(&timeoutList, itemListPtr)) {
  862.         if (Time_GT(itemPtr->time, curTime)) {
  863.         /*
  864.          * The first routine is not ready yet so all the other
  865.          * routines aren't ready either. We are done for now.
  866.          */
  867.          
  868.         break;
  869.  
  870.         } else {
  871.  
  872.         /*
  873.          * First remove the item before calling it. This allows 
  874.          * the routine to call Fs_TimeoutHandlerDestroy or
  875.          * Fs_TimeoutHandlerCreate without messing up the 
  876.          * list pointers.
  877.          */
  878.  
  879.         (List_Links *) readyPtr = itemListPtr;
  880.         itemListPtr  = List_Next(itemListPtr);
  881.         if (itemListPtr == NULL) {
  882.             panic(
  883.             "(FsDispatch)CallTimeoutHandler: next item == NULL\n");
  884.         }
  885.         List_Remove((List_Links *) readyPtr);
  886.  
  887.  
  888.         if (readyPtr->proc == NULL) {
  889.             panic("(FsDispatch)CallTimeoutHandler: routine == NULL\n");
  890.         } else {
  891.             (readyPtr->proc) (readyPtr->data, readyPtr->time);
  892.         }
  893.  
  894.         /*
  895.          * If the routine is to be called again automatically,
  896.          * compute the next time to run and insert it back in the
  897.          * queue. Otherwise, free the element because it isn't 
  898.          * needed any more.
  899.          */
  900.  
  901.         if (!(readyPtr->resched)) {
  902.             free((char *) readyPtr);
  903.         } else{
  904.             List_Links    *tempPtr;
  905.  
  906.             Time_Add(curTime, readyPtr->interval, &(readyPtr->time));
  907.  
  908.             tempPtr = List_First(&timeoutList);
  909.             while (!List_IsAtEnd((&timeoutList), tempPtr)) {
  910.             if (Time_GT( ((TimeoutInfo *)tempPtr)->time, 
  911.                     readyPtr->time)) {
  912.                 break;
  913.             }
  914.             tempPtr = List_Next(tempPtr);
  915.             }
  916.             List_InitElement((List_Links *) readyPtr);
  917.             List_Insert((List_Links *) readyPtr, LIST_BEFORE(tempPtr));
  918.         }
  919.         }
  920.     } /* while */
  921.     } 
  922. }
  923.  
  924.  
  925. /*
  926.  *----------------------------------------------------------------------
  927.  *
  928.  * ComputeTimeoutValue --
  929.  *
  930.  *    Compute when the first routine on the timeout queue needs to be 
  931.  *    called. The value returned in *timeoutPtr is the amount time
  932.  *    that must past before the first routine is called. If a routine
  933.  *    needs to be called now, we return TRUE, otherwise a return value
  934.  *    of FALSE means nothing's ready to be run yet.
  935.  *
  936.  * Results:
  937.  *    TRUE        - routine(s) on the timeout queue need to be run now.
  938.  *    FALSE        - nothing needs to be run yet.
  939.  *
  940.  * Side effects:
  941.  *    None.
  942.  *
  943.  *----------------------------------------------------------------------
  944.  */
  945.  
  946. static Boolean
  947. ComputeTimeoutValue(timeoutPtr)
  948.     Time    *timeoutPtr;
  949. {
  950.     Time    firstTime;
  951.     Time    curTime;
  952.  
  953.     if (List_IsEmpty(&timeoutList)) {
  954.     /*
  955.      * Nothing on the timeout queue so return a very long timeout value.
  956.      */
  957.  
  958.     *timeoutPtr = forever;
  959.     return(FALSE);
  960.  
  961.     } else {
  962.     (void) gettimeofday((struct timeval *) &curTime,
  963.         (struct timezone *) NULL);
  964.     firstTime = ((TimeoutInfo *) (List_Next(&timeoutList)))->time;
  965.  
  966.     if (Time_LE(firstTime, curTime)) {
  967.         /*
  968.          * The callback time of the first routine has already past.
  969.          */
  970.  
  971.         return(TRUE);
  972.     } else {
  973.         /*
  974.          * The callback time of the first routine is in the future.
  975.          */
  976.  
  977.         Time_Subtract(firstTime, curTime, timeoutPtr);
  978.         return(FALSE);
  979.     }
  980.     }
  981. }
  982. @
  983.  
  984.  
  985. 1.6
  986. log
  987. @Didn't handle signals in select right.
  988. @
  989. text
  990. @d22 1
  991. a22 1
  992. static char rcsid[] = "$Header: fsDispatch.c,v 1.3 88/09/23 09:24:16 mendel Exp $ SPRITE (Berkeley)";
  993. a310 3
  994.         if (!infoPtr->inUse) {
  995.         panic("Fs_Dispatch: stream %d not in use\n", streamID);
  996.         } 
  997. d312 8
  998. a345 4
  999.         if (!infoPtr->inUse) {
  1000.         panic("Fs_Dispatch: stream %d not in use\n", streamID);
  1001.         } 
  1002.  
  1003. a367 4
  1004.         if (!infoPtr->inUse) {
  1005.         panic("Fs_Dispatch: stream %d not in use\n", streamID);
  1006.         } 
  1007.  
  1008. d574 1
  1009. @
  1010.  
  1011.  
  1012. 1.5
  1013. log
  1014. @Switched to standard UNIX library procedures wherever possible.
  1015. @
  1016. text
  1017. @d276 4
  1018. a279 2
  1019.     fprintf(stderr, "Fs_Dispatch select error: %s\n", strerror(errno));
  1020.     exit(1);
  1021. @
  1022.  
  1023.  
  1024. 1.4
  1025. log
  1026. @Added extra consistency check.
  1027. @
  1028. text
  1029. @d26 1
  1030. a26 1
  1031. #include <status.h>
  1032. d30 2
  1033. a31 2
  1034. #include <time.h>
  1035. #include <sys.h>
  1036. d34 1
  1037. a34 1
  1038. #include <sync.h>
  1039. d56 1
  1040. a56 1
  1041.  * Bit arrays for use with Fs_Select. ReadMask, writeMask and exceptMask
  1042. d137 1
  1043. a137 1
  1044.  *    the timer queue of procedures and the Fs_Select bitmasks. 
  1045. d168 1
  1046. a168 1
  1047.  *      This routine calls Fs_Select to wait for a timeout or for a stream
  1048. d170 1
  1049. a170 1
  1050.  *      pending.  It looks at the results returned by Fs_Select, and calls
  1051. d182 1
  1052. a182 1
  1053.  *    single Fs_Select call.
  1054. a195 1
  1055.     ReturnStatus    status;
  1056. d239 2
  1057. a240 1
  1058.         (void) Sync_WaitTime(timeout);
  1059. a262 2
  1060.     status = Fs_Select(maxPossNumStreams, timeoutPtr, tempReadMask, 
  1061.                 tempWriteMask, tempExceptMask, &numReady);
  1062. d264 4
  1063. a267 1
  1064.     if (status == FS_TIMEOUT) {
  1065. d275 3
  1066. a277 3
  1067.     } else if (status != SUCCESS) {
  1068.     Stat_PrintMsg(status, "Fs_Dispatch (select)");
  1069.     exit(status);
  1070. d681 2
  1071. a682 1
  1072.     (void) Sys_GetTimeOfDay(&curTime, NULL, NULL);
  1073. d803 2
  1074. a804 1
  1075.     Sys_GetTimeOfDay(&curTime, NULL, NULL);
  1076. d908 2
  1077. a909 1
  1078.     (void) Sys_GetTimeOfDay(&curTime, NULL, NULL);
  1079. @
  1080.  
  1081.  
  1082. 1.3
  1083. log
  1084. @Fixed bug in re-inserting interval callbacks into sorted list.
  1085. @
  1086. text
  1087. @d22 1
  1088. a22 1
  1089. static char rcsid[] = "$Header: fsDispatch.c,v 1.2 88/07/25 10:42:35 ouster Exp $ SPRITE (Berkeley)";
  1090. d824 4
  1091. @
  1092.  
  1093.  
  1094. 1.2
  1095. log
  1096. @Lint.
  1097. @
  1098. text
  1099. @d22 1
  1100. a22 1
  1101. static char rcsid[] = "$Header: fsDispatch.c,v 1.1 88/07/01 09:40:57 ouster Exp $ SPRITE (Berkeley)";
  1102. d847 1
  1103. a847 1
  1104.             tempPtr = itemListPtr;
  1105. d849 1
  1106. a849 1
  1107.             if (Time_LT( ((TimeoutInfo *)tempPtr)->time, 
  1108. @
  1109.  
  1110.  
  1111. 1.1
  1112. log
  1113. @Initial revision
  1114. @
  1115. text
  1116. @d22 1
  1117. a22 1
  1118. static char rcsid[] = "$Header: fsDispatch.c,v 2.0 87/08/11 09:33:29 brent Exp $ SPRITE (Berkeley)";
  1119. d32 1
  1120. @
  1121.